package bb.lanxing.manager;

import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import bb.lanxing.App;
import bb.lanxing.calc.data.DisplayPoint;
import bb.lanxing.common.engin.util.EnginUtil;
import bb.lanxing.devices.utils.DeviceServiceChecker;
import bb.lanxing.devices.utils.XZDeviceHelper;
import bb.lanxing.lib.devices.api.ConnectionListener;
import bb.lanxing.lib.devices.api.DeviceManager;
import bb.lanxing.lib.devices.api.SmartDevice;
import bb.lanxing.service.IWorkoutRemoteService;
import bb.lanxing.service.WorkoutRemoteService;

/**
 * 所有运动相关service的总管
 */
public class RemoteServiceManager implements ConnectionListener {
    private static final String TAG = "RemoteServiceManager";

    private static final int TIME_INTERVAL = 1000;

    private static final int BIND_STATE_NONE = 0;
    private static final int BIND_STATE_BINDING = 1;
    private static final int BIND_STATE_BOUND = 2;

    private static volatile RemoteServiceManager instance;
    private final Object syncLock = new Object();
    // 开始运动后刷新各项数据的service
    private IWorkoutRemoteService workoutRemoteService;
    // service连接
    private ServiceConnection mServiceConnection;
    // 开始运动后刷新各项数据的定时器
    private Timer displayTimer;
    // service绑定状态
    private int bindState = 0;
    // 各种设备的绑定任务
    private LinkedList<Runnable> boundTask;
    // 运动数据改变的监听set
    private Set<DisplayPointChangedListener> mDisplayPointChangedListenerSet;
    private boolean requestDispatchInBackground = false;

    private RemoteServiceManager() {
    }

    public static RemoteServiceManager getInstance() {
        if (instance == null) {
            synchronized (RemoteServiceManager.class) {
                if (instance == null) {
                    instance = new RemoteServiceManager();
                }
            }
        }
        return instance;
    }

    @Override
    public void onConnectionStateChanged(SmartDevice smartDevice, int newState, int errCode) {
        if (newState == DeviceManager.STATE_CONNECTED) {
            Log.e("onConnectionStateChanged", "DeviceManager.STATE_CONNECTED startDisplayTimer()");
            startDisplayTimer();
        } else if (newState == DeviceManager.STATE_DISCONNECTED && !hasBleDeviceConnected() && !isSporting()) {
            stopDisplayTimer();
        }
    }

    private synchronized void startDisplayTimer() {
        Log.d(TAG, "startDisplayTimer");
        if (displayTimer == null) {
            Timer timer = new Timer();
            displayTimer = timer;
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    if (workoutRemoteService != null) {
                        try {
                            DisplayPoint displayPoint = workoutRemoteService.getDisplayPoint();
                            if (displayPoint != null && mDisplayPointChangedListenerSet != null) {
                                synchronized (syncLock) {
                                    for (DisplayPointChangedListener displayPointChangedListener : mDisplayPointChangedListenerSet) {
                                        displayPointChangedListener.onPointChanged(displayPoint);
                                    }
                                }
                            }
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }, 0L, TIME_INTERVAL);
        }
    }

    private synchronized void stopDisplayTimer() {
        Log.d(TAG, "stopDisplayTimer");
        if (displayTimer != null) {
            displayTimer.cancel();
            displayTimer.purge();
            displayTimer = null;
        }
    }

    public void registerDisplayPointChangedListener(DisplayPointChangedListener displayPointChangedListener) {
        if (mDisplayPointChangedListenerSet == null) {
            mDisplayPointChangedListenerSet = new HashSet<>();
        }
        synchronized (syncLock) {
            mDisplayPointChangedListenerSet.add(displayPointChangedListener);
            startDisplayTimer();
        }
    }

    public void unregisterDisplayPointChangedListener(DisplayPointChangedListener displayPointChangedListener) {
        if (mDisplayPointChangedListenerSet != null) {
            synchronized (syncLock) {
                mDisplayPointChangedListenerSet.remove(displayPointChangedListener);
                if (mDisplayPointChangedListenerSet.isEmpty() && !hasBleDeviceConnected() && !isSporting()) {
                    stopDisplayTimer();
                }
            }
        }
    }

    private void runBindTask() {
        synchronized (syncLock) {
            if (boundTask != null && !boundTask.isEmpty()) {
                Iterator<Runnable> it = boundTask.iterator();
                while (it.hasNext()) {
                    Runnable next = it.next();
                    if (next != null) {
                        next.run();
                    }
                }
                boundTask.clear();
            }
        }
    }

    public void startSport(long workoutId, int[] dataTypes) {
        startWorkoutService(workoutId, dataTypes);
        startDisplayTimer();
    }

    public void stopSport() {
        if (workoutRemoteService != null) {
            try {
                workoutRemoteService.stopSport();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        App.getContext().stopService(new Intent(App.getContext(), WorkoutRemoteService.class));
        if (!hasBleDeviceConnected()) {
            stopDisplayTimer();
        }
    }

    public void pauseSportByUser(boolean pause) {
        if (workoutRemoteService != null) {
            try {
                workoutRemoteService.pauseSportByUser(pause);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    public boolean isSporting() {
        Log.v(TAG, "isSporting, workoutRemoteService = " + workoutRemoteService);
        if (workoutRemoteService != null) {
            try {
                return workoutRemoteService.isSporting();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    private void startWorkoutService(long workoutId, int[] dataTypes) {
        bindWorkoutService();
        Intent intent = new Intent(App.getContext(), WorkoutRemoteService.class);
        intent.putExtra("workout_id", workoutId);
        intent.putExtra("record_open", EnginUtil.isRecordOpen());
        intent.putExtra("simulate_location", EnginUtil.isSimulateLocation());
        intent.putExtra("sport_data_types", dataTypes);
        if (App.getContext().isAppForeground()) {
            App.getContext().startForegroundService(intent);
        }
    }

    public void bindWorkoutService() {
        Log.d(TAG, "bindWorkoutService, bindState = " + bindState);

        if (bindState == BIND_STATE_BINDING || bindState == BIND_STATE_BOUND) {
            return;
        }
        bindState = BIND_STATE_BINDING;
        mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                Log.d(TAG, "WorkoutRemoteService is connected.");
                bindState = BIND_STATE_BOUND;
                workoutRemoteService = IWorkoutRemoteService.Stub.asInterface(iBinder);
                if (SharedManager.getInstance().isUseAgreementAgree()
                        && !DeviceServiceChecker.getInstance().isDeviceServiceStart()) {
                    // fix heartrate no data update
                    DeviceServiceChecker.getInstance().startDeviceServiceAndAutoConnector();
                }
                if (hasBleDeviceConnected()) {
                    startDisplayTimer();
                }
                runBindTask();
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                Log.d(TAG, "WorkoutRemoteService is disconnected.");
                bindState = BIND_STATE_NONE;
                workoutRemoteService = null;
            }
        };
        Intent intentService = new Intent(App.getContext(), WorkoutRemoteService.class);
        App.getContext().bindService(intentService, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    public void unbindWorkoutService() {
        Log.d(TAG, "unbindWorkoutService, bindState = " + bindState);
        if (bindState == BIND_STATE_NONE) {
            return;
        }
        if (workoutRemoteService != null) {
            App.getContext().unbindService(mServiceConnection);
            workoutRemoteService = null;
            mServiceConnection = null;
        }
        bindState = BIND_STATE_NONE;
    }

    public boolean isServiceBind() {
        if (workoutRemoteService == null) {
            Log.d("isServiceBind", "workoutRemoteService == null");
        }
        return bindState == BIND_STATE_BOUND && workoutRemoteService != null;
    }

    public void openBLE() {
        synchronized (syncLock) {
            if (boundTask == null) {
                boundTask = new LinkedList<>();
            }
            boundTask.add(() -> {
                if (workoutRemoteService != null) {
                    try {
                        workoutRemoteService.openBLE();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        if (isServiceBind()) {
            runBindTask();
        }
        XZDeviceHelper.onReady(App.getContext());
    }

    public void closeBLE() {
        XZDeviceHelper.release();
        IWorkoutRemoteService iWorkoutRemoteService = workoutRemoteService;
        if (iWorkoutRemoteService != null) {
            try {
                iWorkoutRemoteService.closeBLE();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    private boolean hasBleDeviceConnected() {
        boolean vRet = false;
        DeviceManager deviceManager = XZDeviceHelper.getDeviceManager();
        if (deviceManager != null) {
            if (deviceManager.isConnected(SmartDevice.TYPE_BICI) || deviceManager.isConnected(SmartDevice.TYPE_BES)
                    || deviceManager.isConnected(SmartDevice.TYPE_CADENCE)
                    || deviceManager.isConnected(SmartDevice.TYPE_VORTEX_CADENCE)
                    || deviceManager.isConnected(SmartDevice.TYPE_CADENCE01)
                    || deviceManager.isConnected(SmartDevice.TYPE_HEARTRATE)
                    || deviceManager.isConnected(SmartDevice.TYPE_HEART_RATE01)
                    || deviceManager.isConnected(SmartDevice.TYPE_INDOOR_BIKE)
                    || deviceManager.isConnected(SmartDevice.TYPE_CROSS_TRAINER)
                    || deviceManager.isConnected(SmartDevice.TYPE_ROWER)
                    || deviceManager.isConnected(SmartDevice.TYPE_POWERRATE)
                    || deviceManager.isConnected(SmartDevice.TYPE_BIKE_TRAINER)
                    || deviceManager.isConnected(SmartDevice.TYPE_HEARTRATE_BELT)
                    || deviceManager.isConnected(SmartDevice.TYPE_Q1)
                    || deviceManager.isConnected(SmartDevice.TYPE_HR_X2_PRO)) {
                vRet = true;
            }
        }
        return vRet;
    }

    public void onForeground() {
        if (!DeviceServiceChecker.getInstance().isDeviceServiceStart()) {
            DeviceServiceChecker.getInstance().startDeviceServiceAndAutoConnector();
        }
        XZDeviceHelper.registerConnectionStateListener(this);
        if (isSporting() || hasBleDeviceConnected()) {
            startDisplayTimer();
        }
    }

    public void onBackground() {
        if (!requestDispatchInBackground) {
            stopDisplayTimer();
            XZDeviceHelper.unregisterConnectionStateListener(this);
            requestDispatchInBackground = false;
        }
    }

    public interface DisplayPointChangedListener {
        void onPointChanged(DisplayPoint displayPoint);
    }
}
